package com.hmy.infrastructure.service.oss;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.hmy.common.exception.ClientException;
import com.hmy.infrastructure.api.bo.response.oss.OssCredentialsResponse;
import com.hmy.infrastructure.api.bo.response.oss.OssSignatureResponse;
import com.hmy.infrastructure.bo.request.OssStoreDirRequest;
import com.hmy.infrastructure.bo.response.OssStoreDirResponse;
import com.hmy.infrastructure.common.properties.OssProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author xiongwei
 * @description: TODO
 * @date 2023/11/22
 */
@Service
@Slf4j
public class OssStsService {
    @Resource
    private OssProperties ossProperties;


    /**
     * 获取oss签名
     *
     * @param dir 上传文件目录
     * @return 返回签名信息
     */
    public OssSignatureResponse getOssSignature(String dir) {

        OssSignatureResponse ossSignatureResponse = new OssSignatureResponse();

        String endpoint = ossProperties.getEndpoint();
        String accessKeyId = ossProperties.getAccessKeyId();
        String accessKeySecret = ossProperties.getAccessKeySecret();
        String bucket = ossProperties.getBucketName();

        // host的格式为 bucketname.endpoint
        String host = "https://" + bucket + "." + endpoint;


        OSSClient client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        try {
            long expireTime = ossProperties.getExpireTime();
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String policy = BinaryUtil.toBase64String(binaryData);
            String signature = client.calculatePostSignature(postPolicy);

            ossSignatureResponse.setSignature(signature);
            ossSignatureResponse.setPolicy(policy);
            ossSignatureResponse.setAccessId(accessKeyId);
            ossSignatureResponse.setHost(host);
            ossSignatureResponse.setExpire(String.valueOf(expireEndTime / 1000));
            ossSignatureResponse.setDir(dir);

            return ossSignatureResponse;
        } catch (Exception e) {
            throw new ClientException("获取oss签名失败");
        }
    }

    public OssCredentialsResponse getCredentials() throws ClientException {
        String stsEndpoint = ossProperties.getStsEndpoint();
        String accessKeyId = ossProperties.getAccessKeyId();
        String accessKeySecret = ossProperties.getAccessKeySecret();
        String roleArn = ossProperties.getRoleArn();
        try {
            // 添加endpoint（直接使用STS endpoint，前两个参数留空，无需添加region ID）
            DefaultProfile.addEndpoint("", "", "Sts", stsEndpoint);

            // 构造default profile（参数留空，无需添加region ID）
            IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
            // 用profile构造client
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName("session-name");
            request.setPolicy(null);
            request.setDurationSeconds(3600L);
            final AssumeRoleResponse response = client.getAcsResponse(request);
            AssumeRoleResponse.Credentials credentials = response.getCredentials();

            log.info("credentials:{}", credentials.getSecurityToken());

            OssCredentialsResponse ossCredentialsResponse = new OssCredentialsResponse();
            BeanUtils.copyProperties(credentials, ossCredentialsResponse);
            ossCredentialsResponse.setBucketName(ossProperties.getBucketName());
            ossCredentialsResponse.setEndpoint(ossProperties.getEndpoint());
            return ossCredentialsResponse;
        } catch (Exception e) {
            log.info("获取OSS临时认证异常:", e);
            throw new ClientException("获取OSS认证异常");
        }
    }

    public OssStoreDirResponse getOssStoreDir(OssStoreDirRequest storeDirRequest) {
        Runnable exceptionThrow = () -> {
            throw new ClientException("OSS存储配置信息不存在");
        };
        List<OssProperties.StoreInfo> storeInfos = ossProperties.getStoreInfos();
        if (StringUtils.isEmpty(storeDirRequest.getServiceKey()) || storeInfos.isEmpty()) {
            exceptionThrow.run();
        }
        Optional<OssProperties.StoreInfo> storeDirOptional = storeInfos.stream().filter(i -> i.getServiceName().equals(storeDirRequest.getServiceKey())).findFirst();
        AtomicReference<OssStoreDirResponse> response = new AtomicReference<>();
        storeDirOptional.ifPresentOrElse(storeInfo -> {
            List<OssProperties.BusinessDir> businessDirs = storeInfo.getBusinessDirs();
            if (businessDirs.isEmpty()) {
                exceptionThrow.run();
            }
            Optional<OssProperties.BusinessDir> businessDirOptional = businessDirs.stream().filter(businessDir -> businessDir.getBusinessKey().equals(storeDirRequest.getBusinessKey())).findFirst();
            businessDirOptional.ifPresentOrElse(businessDir -> {
                response.set(new OssStoreDirResponse(businessDir.getStoreDir()));
            }, exceptionThrow);

        }, exceptionThrow);
        return response.get();
    }
}
